<?php

namespace app\common\traits;

use think\db\Query;
use think\Model;
use think\model\Collection;
use think\Paginator;

trait ModelQuery
{

    public $model;

    public $enable = 1; // 开启

    public $disable = 0; // 关闭

    /**
     * @param array $params
     * @param bool $withTrashed
     * @return Query
     */
    public function listQuerySetting(array $params, bool $withTrashed = false): Query
    {
        $query = $withTrashed ? $this->model::withTrashed() : $this->model->getQuery();
        if ($params['field'] ?? false) {
            $query->field($params['field']);
        }
        if (isset($params['with'])) {
            $query->with($params['with']);
        }
        $query = $this->handleOrder($query, $params);
        return $this->handleSearch($query, $params);
    }

    /**
     * @return Model
     */
    public function getModel(): Model
    {
        return new $this->model;
    }

    /**
     * 过滤数据库不存在的字段[二维数组]
     * @param array $fields
     * @param bool $removePk
     * @return array
     */
    protected function filterQueryAttributes(array $fields, bool $removePk = false): array
    {
        $filterKeys = array_keys($fields);
        $this->filterExecuteAttributes($filterKeys, $removePk);
        foreach ($fields as $k => $item) {
            if (!in_array($k, $filterKeys)) {
                unset($fields[$k]);
            }
        }
        return empty($fields) ? ['*'] : $fields;
    }

    /**
     * 过滤数据库不存在的字段[一维数组]
     * @param array $data
     * @param bool $removePk
     * @return void
     */
    protected function filterExecuteAttributes(array &$data, bool $removePk = false): void
    {
        $model = new $this->model;
        $attrs = $model->checkAllowField();
        foreach ($data as $name) {
            if (!in_array($name, $attrs)) {
                unset($data[$name]);
            }
        }
        if ($removePk && isset($data[$model->getPk()])) {
            unset($data[$model->getPk()]);
        }
        $model = null;
    }

    /**
     * 处理排序
     * @param Query $query
     * @param array|null $params
     * @return Query
     */
    public function handleOrder(Query $query, ?array &$params = null): Query
    {
        // 对树型数据强行加个排序
        if (isset($params['tree'])) {
            $query->order($params['tree_pid']);
        }

        if ($params['orderBy'] ?? false) {
            if (is_array($params['orderBy'])) {
                foreach ($params['orderBy'] as $key => $order) {
                    $query->order($order, $params['orderType'][$key] ?? 'asc');
                }
            } else {
                $query->order($params['orderBy'], $params['orderType'] ?? 'asc');
            }
        }

        return $query;
    }

    /**
     * 搜索条件处理器
     * @param Query $query
     * @param array $params
     * @return Query
     */
    public function handleSearch(Query $query, array $params): Query
    {
        return $query;
    }

    /**
     * 根据主键读取一条数据
     * @param int $id
     * @return Model|null
     */
    public function read(int $id): ?Model
    {
        return $this->model->find($id);
    }

    /**
     * 根据主键读取一条数据(包含软删除数据)
     * @param int $id
     * @return Model|null
     */
    public function readWithTrashed(int $id): ?Model
    {
        return $this->model::withTrashed()->find($id);
    }

    /**
     * 根据条件获取一条数据
     * @param array $condition
     * @param array|null $with
     * @return Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function first(array $condition, ?array $with = []): ?Model
    {
        return $this->getModel()->where($condition)->with($with)->find();
    }

    /**
     * 根据条件获取一条数据(包含软删除数据)
     * @param array $condition
     * @param array $with
     * @param array|string[] $column
     * @return Model|null
     */
    public function firstWithTrashed(array $condition, array $with = [], array $column = ['*']): ?Model
    {
        return $this->model::withTrashed()->where($condition)->with($with)->field($column)->find();
    }

    /**
     * 获取单个值
     * @param array $condition
     * @param string $columns
     * @return mixed
     */
    public function value(array $condition, string $columns = 'id')
    {
        return $this->model::where($condition)->value($columns);
    }

    /**
     * 获取单列值
     * @param array $condition
     * @param string $columns
     * @return array
     */
    public function column(array $condition, string $columns = 'id'): array
    {
        return $this->model::where($condition)->column($columns);
    }

    /**
     * 获取列表数据
     * @param array|null $params
     * @param bool $withTrashed
     * @return bool|\PDOStatement|string|\think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function getList(?array $params, bool $withTrashed = false)
    {
        return $this->listQuerySetting($params, $withTrashed)->select();
    }

    /**
     * 获取分页列表数据
     * @param array|null $params
     * @param bool $withTrashed
     * @param int $limit
     * @return Paginator
     * @throws \think\exception\DbException
     */
    public function getPageList(?array $params,  bool $withTrashed = false, int $limit = 10): Paginator
    {
        return $this->listQuerySetting($params, $withTrashed)->paginate($limit);
    }

    /**
     * 更新一条数据
     * @param int $id
     * @param array $data
     * @return bool
     */
    public function update(int $id, array $data): bool
    {
        $this->filterExecuteAttributes($data, true);
        return $this->model::find($id)->update($data);

    }

    /**
     * 按条件更新数据
     * @param array $condition
     * @param array $data
     * @return bool
     */
    public function updateByCondition(array $condition, array $data): bool
    {
        $this->filterExecuteAttributes($data, true);
        return $this->model->getQuery()->where($condition)->update($data) > 0;
    }

    /**
     * 新增数据
     * @param array $data
     * @return int
     */
    public function save(array $data): int
    {
        $this->filterExecuteAttributes($data);
        $model = $this->model::create($data);
        return $model->{$model->getPk()};
    }

    /**
     * 单个或批量软删除数据
     * @param array $ids
     * @return bool
     */
    public function delete(array $ids): bool
    {
        $this->model::destroy($ids);
        return true;
    }
    /**
     * 单个或批量禁用数据
     * @param array $ids
     * @param string $field
     * @return bool
     */
    public function disable(array $ids, string $field = 'status'): bool
    {
        $this->model->getQuery()->whereIn((new $this->model)->getPk(), $ids)->update([$field => $this->disable]);
        return true;
    }

    /**
     * 单个或批量启用数据
     * @param array $ids
     * @param string $field
     * @return bool
     */
    public function enable(array $ids, string $field = 'status'): bool
    {
        $this->model->getQuery()->whereIn((new $this->model)->getPk(), $ids)->update([$field => $this->enable]);
        return true;
    }

    /**
     * 闭包通用查询设置
     * @param \Closure|null $closure 传入的闭包查询
     * @return Query
     */
    public function settingClosure(?\Closure $closure = null): Query
    {
        return $this->model->getQuery()->where(function($query) use($closure) {
            if ($closure instanceof \Closure) {
                $closure($query);
            }
        });
    }

    /**
     * 闭包通用方式查询一条数据
     * @param \Closure|null $closure
     * @param array $column
     * @return array|bool|\PDOStatement|string|Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function one(?\Closure $closure = null, array $column = ['*'])
    {
        return $this->settingClosure($closure)->field($column)->find();
    }

    /**
     * 闭包通用方式查询数据集合
     * @param \Closure|null $closure
     * @param array $column
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function select(?\Closure $closure = null, array $column = ['*']): array
    {
        return $this->settingClosure($closure)->field($column)->select()->toArray();
    }

    /**
     * 多字段检测唯一
     * @param array $dbField
     * @param array $data
     * @param array $where
     * @param array $dataField
     * @param string $key
     * @return bool
     */
    public function checkUnionUnique(array $dbField, array $data, array $where = [], array $dataField = [], string $key = 'id'): bool
    {
        foreach ($dbField as $k) {
            $where[$k] = $data[$k] ?? $data[$dataField[$k]];
        }
        if (isset($data[$key])) { // 判断是否自己有id，排除自己这条
            return empty($this->model->getQuery()->where($key, '<>', $data[$key])->whereExists($where));
        } else {
            return empty($this->model->getQuery()->whereExists($where));
        }

    }
}